/*
 *      net-sim - main
 *
 * 		Router.java
 *
 *      Copyright 23/12/2008 nicolas
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */
package main;
import link.Link;
import link.Links;
import net_interfaces.Interface;
import net_interfaces.Interfaces;
import networkProtocols.NetworkProtocols;
import networkProtocols.ARP.ARP;
import networkProtocols.ICMP.ICMP;
import networkProtocols.IP.Datagram;
import networkProtocols.IP.IP;
import networkProtocols.IP.Address.IpAddress;
import networkProtocols.IP.Address.Mask;
import exceptions.AddressException;
import exceptions.NodeException;

/**
 * Representa un router. Las funciones son (algunas aun no implementadas)
 * 1-Leer de un archivo los links del router (aun no implementado, se utiliza un unico link
 *   cuyas caracteristicas se ingresan como parametros al main
 * 2-Instanciar los diferentes elementos en el router
 */
public class Router {

  /** The links. */
  static Links links;            // Links del router

  /** The ifs. */
  static Interfaces ifs;         // Interfaces de red

  /** The Nt. */
  static NetworkProtocols Nt;    // Elementos comunes a los protocolos del nivel de red

  /** The Ip. */
  static IP Ip;                  // IP del nodo

  /** The Arp. */
  static ARP Arp;                // ARP del nodo

  /** The Icmp. */
  static ICMP Icmp;              // ICMP del nodo

  /**
   * The main method.
   *
   * @param args the arguments
   *
   * @throws NodeException the node exception
   * @throws AddressException the address exception
   */
  public static void main(String[] args)  throws NodeException, AddressException {

    // Chequeo de parametros y direcciones de hosts.
    // Por el momento se lee la direccion de un unico link, parte local y remota.
    // Luego esto estara en un archivo con la definicion de links para cad arouter
    if(args.length != 4 ) throw new NodeException(NodeException.PARAMETROS);

    // Creacion de los links, por ahora uno, habria que pasar mas parametros al main
    // o leer desde un archivo
    links = new Links();
    Link lnk0 = links.addLink("lnk0", args[0], args[1], args[2], args[3]);

    // Creacion de interfaces
    ifs = new Interfaces();    // Tabla con las interfaces
    Interface eth0 = ifs.addInterface("eth0", new IpAddress("201P0P0P2"), new Mask(new Integer(24)), new Integer(1500));
    Interface eth1 = ifs.addInterface("eth1",new IpAddress("201P0P1P2"), new Mask(new Integer(24)), new Integer(1400));
    Interface eth2 = ifs.addInterface("eth2", new IpAddress("201P0P2P2"), new Mask(new Integer(24)), new Integer(1500));
    Interface eth3 = ifs.addInterface("eth3", new IpAddress("201P0P3P2"), new Mask(new Integer(24)), new Integer(1500));
    ifs.toString();

    // Instanciacion de los elementos comunes a los protocolos de red
    Nt = new NetworkProtocols();

    // Creacion y agregado de los protocolos en el nivel 3
    Ip = new IP(ifs,Nt);    //Creacion de IP
    NetworkProtocols.addProtocol(NetworkProtocols.PROTO_IP, Ip);
    Arp = new ARP(ifs,Nt);    //Creacion de ARP
    NetworkProtocols.addProtocol(NetworkProtocols.PROTO_ARP, Arp);
    Icmp = new ICMP(ifs,Nt);    //Creacion de ICMP
    NetworkProtocols.addProtocol(NetworkProtocols.PROTO_ICMP, Icmp);

    // 2-Creacion de entradas en la tabla de ruteo de IP (esto lo haria el nivel superior a traves
    // de un evento local a IP, por ejemplo, el adm de red llena la tabla de ruteo)
    Ip.addRoute(new IpAddress("201P0P0P0"),new Mask(new Integer(24)), true, new IpAddress("201P0P0P1"), eth0);
    Ip.addRoute(new IpAddress("201P0P1P0"),new Mask(new Integer(24)), true, new IpAddress("201P0P1P1"), eth1);
    Ip.addRoute(new IpAddress("201P0P2P0"),new Mask(new Integer(24)), true, new IpAddress("201P0P2P1"), eth2);
    Ip.addRoute(new IpAddress("201P0P3P0"),new Mask(new Integer(24)), true, new IpAddress("201P0P3P3"), eth3);

    Ip.addDefault(new IpAddress("201P0P3P2"), eth3);
    System.out.println(" ");
    System.out.println("Tabla de ruteo");
    Ip.rt();
    System.out.println(" ");

    //Ip.delRoute(new IpAddress("111P22P0P0"),new Mask(new Integer(16)));
    //Ip.addRoute(new IpAddress("111P22P255P255"),new Mask(new Integer(18)), true, new IpAddress("3P70P8P7"), eth1);
    // Ip.rt();


    // Asociacion de las interfaces con los links
    lnk0.setInterface(eth0);
    /*
    RoutingEntry r1 = Ip.rTable.getNextHop(new IpAddress("201P0P1P6") );
    if(r1 != null) System.out.println("Matching" + r1.toString());
    RoutingEntry r2 = Ip.rTable.getNextHop(new IpAddress("211P22P7P6") );
    if(r2 != null) System.out.println("Matching" + r2.toString());;
     */

    // Prueba, se construye un datagram IP, se lo muestra y luego se invoca a la interfaz
    // para que lo envie. El protocolo de N3 (en este caso seria IP) pone su PROTO id como
    // parametro adicional; en la recepcion, la interfaz de arribo en base a este proto id
    // enregara el frame al ip
    Datagram f = new Datagram(4, 11, 6, true, false, false, false, false, 12, 4567, false, true, true,
                              8123, 232, 5, 12336, new IpAddress("2P22P3P3"), new IpAddress("3P33P2P34"));
    f.genChecksum();          // Genera checksum correcto
    byte[] v = (byte[]) f.toByte();
    String h = f.toString();
    System.out.println(" Datagram con chk correcto generado por router " + h);
    eth0.send(NetworkProtocols.PROTO_IP, v);

    Datagram f1 = new Datagram(4, 11, 6, true, false, false, false, false, 12, 4567, false, true, true,
                              8123, 232, 5, 12336, new IpAddress("2P22P3P3"), new IpAddress("3P33P2P34"));
    f1.genBadChecksum();          // Genera checksum incorrecto
    byte[] v1 = (byte[]) f1.toByte();
    String h1 = f1.toString();
    System.out.println(" Datagram con chk incorrecto generado por router " + h1);
    eth0.send(NetworkProtocols.PROTO_IP, v1);

    //byte[] ll = new byte[6];
    //ll[0]=0x66 ;ll[1]= 0x66 ;ll[2]= 0x55 ;  ll[3]=0x55 ;ll[4]= 0x0f ;ll[5]= 0x0f ;
    //f1.borrar(ll);
  }

}
